﻿using System;
using System.Collections.Generic;
using System.Text;
using System.Collections;
using System.Net.Sockets;
using System.Net;
using System.Diagnostics;

namespace WaterKit.TcpUdp
{
    /// <summary>
    /// Tcp通讯服务端
    /// </summary>
    public class TcpServer
    {
        #region 字段

        /// <summary>
        /// 默认支持客户连接数
        /// </summary>
        ushort DefaultMaxClient = 1024;

        /// <summary>
        /// 接收数据缓冲区大小64K
        /// </summary>
        public const int DefaultBufferSize = 64 * 1024;

        /// <summary>
        /// 最大数据报文大小
        /// </summary>
        public const int MaxDatagramSize = 640 * 1024;

        /// <summary>
        /// 服务器端口
        /// </summary>
        private ushort _port;

        /// <summary>
        /// 服务器允许最大连接数
        /// </summary>
        private ushort _maxClient;

        /// <summary>
        /// 服务器运行状态
        /// </summary>
        private bool _isRun;

        /// <summary>
        /// 接收数据缓冲区
        /// </summary>
        private byte[] _recvDataBuffer;

        /// <summary>
        /// 服务器使用的异步Socket类,
        /// </summary>
        private Socket _svrSock;

        /// <summary>
        /// 所有客户端会话的哈希表
        /// </summary>
        private Hashtable _sessionTable;

        /// <summary>
        /// 当前连接的客户端数
        /// </summary>
        private ushort _clientCount;

        /// <summary>
        /// 报文结尾标记
        /// </summary>
        private string endTag = "]}";

        #endregion

        #region 事件

        /// <summary>
        /// 客户端连接事件
        /// </summary>
        public event NetEvent ClientConn;
        /// <summary>
        /// 客户端强制关闭事件
        /// </summary>
        public event NetEvent ClientClose;
        /// <summary>
        /// 服务端已满事件
        /// </summary>
        public event NetEvent ServerFull;
        /// <summary>
        /// 服务端接收数据事件
        /// </summary>
        public event NetEvent RecvData;

        #endregion

        #region 构造函数

        /// <summary>
        /// 构造函数
        /// </summary>
        /// <param name="port">服务器端监听的端口号</param>
        /// <param name="maxClient">服务器能容纳客户端的最大能力</param>
        public TcpServer(ushort port, ushort maxClient)
        {
            _port = port;
            _maxClient = maxClient;
        }

        #endregion

        #region 属性

        /// <summary>
        /// 服务器最大连接数
        /// </summary>
        public ushort MaxClient
        {
            get
            {
                return _maxClient == 0 ? DefaultMaxClient : _maxClient;
            }
            set { _maxClient = value; }
        }

        /// <summary>
        /// 服务器运行状态
        /// </summary>
        public bool IsRun
        {
            get
            {
                return _isRun;
            }
        }

        #endregion

        #region 公共方法

        /// <summary>
        /// 开启服务器
        /// </summary>
        public virtual void Start()
        {
            if (_isRun)
            {
                throw (new ApplicationException("服务器已经运行"));
            }
            _sessionTable = new Hashtable(MaxClient);
            _recvDataBuffer = new byte[DefaultBufferSize];
            _svrSock = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);

            IPEndPoint iep = new IPEndPoint(IPAddress.Any, _port);
            _svrSock.Bind(iep);
            _svrSock.Listen(1024);
            _svrSock.BeginAccept(new AsyncCallback(AcceptConn), _svrSock);

            _isRun = true;
        }

        /// <summary>
        /// 停止服务器程序,所有与客户端的连接将关闭
        /// </summary>
        public virtual void Stop()
        {
            if (!_isRun)
            {
                throw (new ApplicationException("服务器已经停止"));
            }
            _isRun = false;
            if (_svrSock.Connected)
            {
                _svrSock.Shutdown(SocketShutdown.Both);
            }

            CloseAllClient();
            _svrSock.Close();
            _sessionTable = null;
        }

        /// <summary>
        /// 关闭所有的客户端会话,与所有的客户端连接会断开
        /// </summary>
        public virtual void CloseAllClient()
        {
            foreach (Session client in _sessionTable.Values)
            {
                client.Close();
            }

            _clientCount = 0;
            _sessionTable.Clear();
        }

        /// <summary>
        /// 关闭一个与客户端之间的会话
        /// </summary>
        /// <param name="closeClient">需要关闭的客户端会话对象</param>
        public virtual void CloseSession(Session closeClient)
        {
            Debug.Assert(closeClient != null);
            if (closeClient != null)
            {
                closeClient.Datagram = null;
                _sessionTable.Remove(closeClient.ID);
                _clientCount--;

                //客户端强制关闭连接
                if (ClientClose != null)
                {
                    ClientClose(this, new NetEventArgs(closeClient));
                }
                closeClient.Close();
            }
        }

        /// <summary>
        /// 异步发送数据
        /// </summary>
        /// <param name="recvDataClient">接收数据的客户端会话</param>
        /// <param name="datagram">数据报文</param>
        public virtual void AsyncSend(Session recvDataClient, string datagram)
        {
            //使用UTF-8编号
            byte[] data = Encoding.UTF8.GetBytes(datagram+endTag);
            //异步发送数据
            recvDataClient.ClientSocket.BeginSend(data, 0, data.Length, SocketFlags.None,new AsyncCallback(SendDataEnd), recvDataClient.ClientSocket);
        }

        /// <summary>
        /// 客户端会话数组,保存所有的客户端,不允许对该数组的内容进行修改
        /// </summary>
        public Hashtable SessionTable
        {
            get
            {
                return _sessionTable;
            }
        }

        #endregion

        #region 私有方法

        /// <summary>
        /// 发送数据完成处理函数
        /// </summary>
        /// <param name="iar">目标客户端Socket</param>
        protected virtual void SendDataEnd(IAsyncResult iar)
        {
            Socket client = (Socket)iar.AsyncState;

            int sent = client.EndSend(iar);
        }

        /// <summary>
        /// 客户端连接处理函数
        /// </summary>
        /// <param name="iar">欲建立服务器连接的Socket对象</param>
        protected virtual void AcceptConn(IAsyncResult iar)
        {
            //如果服务器停止了服务,拒绝连接请求
            if (!_isRun)
            {
                return;
            }

            //接受一个客户端的连接请求
            Socket oldserver = (Socket)iar.AsyncState;
            Socket client = oldserver.EndAccept(iar);

            //检查是否达到最大的允许的客户端数目
            if (_clientCount == _maxClient)
            {
                if (ServerFull != null)
                {
                    ServerFull(this, new NetEventArgs(new Session(client)));
                }
            }
            else
            {
                //新建一个客户端连接
                Session newSession = new Session(client);
                _sessionTable.Add(newSession.ID, newSession);

                _clientCount++;

                newSession.RecvDataBuffer = new byte[16 * 1024];
                //开始接受来自该客户端的数据
                client.BeginReceive(newSession.RecvDataBuffer, 0, newSession.RecvDataBuffer.Length, SocketFlags.None,
                 new AsyncCallback(ReceiveData), newSession);

                //新的客户段连接,发出通知
                if (ClientConn != null)
                {
                    ClientConn(this, new NetEventArgs(newSession));
                }
            }

            //继续接受客户端
            _svrSock.BeginAccept(new AsyncCallback(AcceptConn), _svrSock);
        }

        /// <summary>
        /// 通过Socket对象查找Session对象
        /// </summary>
        /// <param name="client"></param>
        /// <returns>找到的Session对象,如果为null,说明并不存在该会话</returns>
        private Session FindSession(Socket client)
        {
            SessionId id = new SessionId((int)client.Handle);
            return (Session)_sessionTable[id];
        }

        /// <summary>
        /// 接受数据完成处理函数，异步的特性就体现在这个函数中，
        /// 收到数据后，会自动解析为字符串报文
        /// </summary>
        /// <param name="iar">目标客户端Socket</param>
        protected virtual void ReceiveData(IAsyncResult iar)
        {
            Session sendDataSession = (Session)iar.AsyncState;
            Socket client = sendDataSession.ClientSocket;
            try
            {
                //如果两次开始了异步的接收,所以当客户端退出的时候
                //会两次执行EndReceive
                int recv = client.EndReceive(iar);

                if (recv == 0)
                {
                    CloseClient(client, Session.ExitType.NormalExit);
                    return;
                }

                string receivedData = Encoding.UTF8.GetString(sendDataSession.RecvDataBuffer, 0, recv);

                //将接收信息发送至绑定事件
                if (RecvData != null)
                {
                    Debug.Assert(sendDataSession != null);
                    if (sendDataSession.Datagram != null && sendDataSession.Datagram.Length != 0)
                    {
                        receivedData = sendDataSession.Datagram + receivedData;
                    }

                    string[] recvDatagrams = Resolve(ref receivedData);

                    foreach (string newDatagram in recvDatagrams)
                    {
                        ICloneable copySession = (ICloneable)sendDataSession;
                        Session clientSession = (Session)copySession.Clone();

                        string strDatagram = newDatagram;
                        clientSession.Datagram = strDatagram;

                        //发布报文消息
                        RecvData(this, new NetEventArgs(clientSession));
                    }

                    //剩余的代码片断,下次接收的时候使用
                    sendDataSession.Datagram = receivedData;

                    if (sendDataSession.Datagram.Length > MaxDatagramSize)
                    {
                        sendDataSession.Datagram = null;
                    }
                }
                else
                {
                    ICloneable copySession = (ICloneable)sendDataSession;
                    Session clientSession = (Session)copySession.Clone();
                    clientSession.Datagram = receivedData;

                    RecvData(this, new NetEventArgs(clientSession));
                }

                //继续异步接收
                client.BeginReceive(sendDataSession.RecvDataBuffer, 0, sendDataSession.RecvDataBuffer.Length, SocketFlags.None,
                 new AsyncCallback(ReceiveData), sendDataSession);
            }
            catch (SocketException ex)
            {
                if (10054 == ex.ErrorCode)
                {
                    //客户端强制关闭
                    CloseClient(client, Session.ExitType.ExceptionExit);
                }
            }
            catch (ObjectDisposedException ex)
            {
                if (ex != null)
                {
                    ex = null;
                }
            }
        }

        /// <summary>
        /// 解析报文,主要是将报文判断结尾标记
        /// </summary>
        /// <param name="rawDatagram"></param>
        /// <returns></returns>
        protected virtual string[] Resolve(ref string rawDatagram)
        {
            ArrayList datagrams = new ArrayList();
            //末尾标记位置索引
            int tagIndex = -1;

            while (true)
            {
                tagIndex = rawDatagram.IndexOf(endTag, tagIndex + 1);
                if (tagIndex == -1)
                {
                    break;
                }
                else
                {
                    //按照末尾标记把字符串分为左右两个部分
                    string newDatagram = rawDatagram.Substring(0, tagIndex + endTag.Length);
                    datagrams.Add(newDatagram);
                    if (tagIndex + endTag.Length >= rawDatagram.Length)
                    {
                        rawDatagram = "";
                        break;
                    }
                    rawDatagram = rawDatagram.Substring(tagIndex + endTag.Length, rawDatagram.Length - newDatagram.Length);
                    //从开始位置开始查找
                    tagIndex = 0;
                }
            }
            string[] results = new string[datagrams.Count];
            datagrams.CopyTo(results);
            return results;
        }

        /// <summary>
        /// 关闭一个客户端Socket,首先需要关闭Session
        /// </summary>
        /// <param name="client">目标Socket对象</param>
        /// <param name="exitType">客户端退出的类型</param>
        protected virtual void CloseClient(Socket client, Session.ExitType exitType)
        {
            Debug.Assert(client != null);

            //查找该客户端是否存在,如果不存在,抛出异常
            Session closeClient = FindSession(client);

            closeClient.TypeOfExit = exitType;

            if (closeClient != null)
            {
                CloseSession(closeClient);
            }
            else
            {
                throw (new ApplicationException("需要关闭的Socket对象不存在"));
            }
        }
        #endregion
    }
}
